﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using Microsoft.WindowsAzure.Diagnostics;
using Microsoft.WindowsAzure.ServiceRuntime;
using smarx.HostedWebCore;
using System.Collections.Specialized;
using System.IO;
using Microsoft.WindowsAzure;

namespace WorkerRole
{
    public class WorkerRole : RoleEntryPoint
    {

        private const string ElectionServiceEndpoint = "MasterElectionService";
        private const int QueryIntervalInSeconds = 10;
        private const int RetryLimit = 6;
        private string instanceId = string.Empty;

        private RoleInstance ThisInstance { get; set; }
  
        private HWCServer hwcServer;
        private DiagnosticMonitor diagnosticMonitor;

        #region Added By Sudhir
        StringWriter output = new StringWriter();
        StringWriter error = new StringWriter();
        Timer timer = null;
        #endregion

        public override void Run()
        {
            //Thread.Sleep(Timeout.Infinite);
            Trace.WriteLine("WorkerRole entry point called", "Information");

            //  for (; ; )
            //   {

            string result = string.Empty;
            // This is a sample worker implementation. Replace with your logic.
            Trace.WriteLine("WorkerRole entry point called", "Information");

            string time = string.Empty;

            //Get the duration from config file
            try
            {
                time = RoleEnvironment.GetConfigurationSettingValue("Duration");

                time = time.Trim();

                //If no value exists for Duration
                if (time == null || time.Length == 0)
                {
                    //Execute once if duration node value empty
                    Execute(null);
                }
                else
                {
                    string[] splitTime = time.Split(':');

                    //Set the timer
                    TimeSpan ts = new TimeSpan(Convert.ToInt32(splitTime[0]), Convert.ToInt32(splitTime[1]), Convert.ToInt32(splitTime[2]));
                    System.Threading.TimerCallback cb = new TimerCallback(Execute);
                    timer = new System.Threading.Timer(cb, null, new TimeSpan(0), ts);
                }
            }
            catch (Exception ex)
            {
                //Execute once if the duration node is not present
                Execute(null);
                Trace.WriteLine(ex.Message, "Error");
            }

            Trace.WriteLine("Working", "Information");
            while (true)
            {
                Thread.Sleep(10000);
                Trace.WriteLine("Working", "Information");
            }
        }


        //public override bool OnStart()
        //{


        //    return base.OnStart();
        //    // RoleEnvironment.GetConfigurationSettingValue("StartupScript")
        //}

        public override bool OnStart()
        {
            bool UseHostedCore = false;
            var cfg = DiagnosticMonitor.GetDefaultInitialConfiguration();
            var approot = Environment.GetEnvironmentVariable("RoleRoot") + @"\approot";
            string configPath = RoleEnvironment.GetLocalResource("Config").RootPath;

            // Set the maximum number of concurrent connections 
            ServicePointManager.DefaultConnectionLimit = 12;

            DiagnosticMonitor.Start("DiagnosticsConnectionString");

            // For information on handling configuration changes
            // see the MSDN topic at http://go.microsoft.com/fwlink/?LinkId=166357.
            RoleEnvironment.Changing += CSWRoleEnvironmentChanging;


            try
            {
                UseHostedCore = Convert.ToBoolean(RoleEnvironment.GetConfigurationSettingValue("UseHostedCore"));

                if (UseHostedCore == true)
                {

                    // Setup PHP Tuntime
                    SetupPHPRuntime(approot);


                    // HWC uses the event log to indicate what's broken.  This config setting is really handy when debugging bad config.
                    cfg.WindowsEventLog.DataSources.Add("Application!*[System[Provider[@Name='HostableWebCore']]]");

                    // Monitor PHP logs
                    DirectoryConfiguration phpLogs = new DirectoryConfiguration();
                    phpLogs.Container = RoleEnvironment.GetConfigurationSettingValue("PHPLogsContainerName");
                    phpLogs.Path = approot + @"\php\logs";
                    cfg.Directories.DataSources.Add(phpLogs);
                    cfg.Directories.ScheduledTransferPeriod = TimeSpan.FromSeconds(30);
                    cfg.Logs.ScheduledTransferPeriod = TimeSpan.FromSeconds(30);
                    cfg.Logs.ScheduledTransferLogLevelFilter = LogLevel.Verbose;

                
                }
            }
            catch
            {
            }



            if (UseHostedCore == false)
            {
                return base.OnStart();
            }

            diagnosticMonitor = DiagnosticMonitor.Start("DiagnosticsConnectionString", cfg);

            RoleEnvironment.Changing += RoleEnvironmentChanging;

            var parameters = new NameValueCollection();
            // "DiagnosticStore" is automatically inserted and is where the diagnostic monitor will look for these
            var diagnosticStore = RoleEnvironment.GetLocalResource("DiagnosticStore").RootPath;
            parameters["LogFilesDirectory"] = Path.Combine(diagnosticStore, "LogFiles");
            parameters["FailedReqLogFilesDirectory"] = Path.Combine(diagnosticStore, "FailedReqLogFiles");
            parameters["iisCompressionCacheDirectory"] = RoleEnvironment.GetLocalResource("iisCompressionCache").RootPath;
            parameters["approot"] = approot;
            var endpoint = RoleEnvironment.CurrentRoleInstance.InstanceEndpoints["HttpIn"].IPEndpoint;
            parameters["address"] = endpoint.Address.ToString();
            parameters["port"] = endpoint.Port.ToString();
            parameters["aspNetTempDirectory"] = RoleEnvironment.GetLocalResource("aspNetTemp").RootPath;
            parameters["applicationPoolName"] = "{" + Guid.NewGuid().ToString() + "}";
            parameters["machineKeyElement"] = RoleEnvironment.GetConfigurationSettingValue("machineKeyElement");

            // substitutes in the above parameters
            WorkerConfig.WriteConfigFile(
                parameters,
                Path.Combine(approot, "applicationHost.config"),
                Path.Combine(configPath, "applicationHost.config"));

            // substitutes in the above parameters
            WorkerConfig.WriteConfigFile(
                parameters,
                Path.Combine(approot, "web.config"),
                Path.Combine(configPath, "web.config"));


            if (UseHostedCore == true)
            {
                hwcServer = new HWCServer(Path.Combine(configPath, "applicationHost.config"), Path.Combine(configPath, "web.config"));
                hwcServer.Start();
            }

            #region Setup CloudStorageAccount Configuration Setting Publisher

            // This code sets up a handler to update CloudStorageAccount instances when their corresponding
            // configuration settings change in the service configuration file.
            CloudStorageAccount.SetConfigurationSettingPublisher((configName, configSetter) =>
            {
                // Provide the configSetter with the initial value
                configSetter(RoleEnvironment.GetConfigurationSettingValue(configName));

                RoleEnvironment.Changed += (sender, arg) =>
                {
                    if (arg.Changes.OfType<RoleEnvironmentConfigurationSettingChange>()
                        .Any((change) => (change.ConfigurationSettingName == configName)))
                    {
                        // The corresponding configuration setting has changed, propagate the value
                        if (!configSetter(RoleEnvironment.GetConfigurationSettingValue(configName)))
                        {
                            // In this case, the change to the storage account credentials in the
                            // service configuration is significant enough that the role needs to be
                            // recycled in order to use the latest settings. (for example, the 
                            // endpoint has changed)
                            RoleEnvironment.RequestRecycle();
                        }
                    }
                };
            });
            #endregion

            ThisInstance = RoleEnvironment.CurrentRoleInstance;
            RoleEnvironment.Stopping += new EventHandler<RoleEnvironmentStoppingEventArgs>(RoleEnvironment_Stopping);

            return base.OnStart();
        }

        // Setup PHP Runtime
        private void SetupPHPRuntime(string approot)
        {
            // Create sessions and logs folders
            Directory.CreateDirectory(approot + @"\php\sessions");
            Directory.CreateDirectory(approot + @"\php\logs");

            // Edit php.ini with correct path at runtime
            var parameters = new NameValueCollection();
            parameters["approot"] = approot;
            // substitutes in the above parameters
            WorkerConfig.WriteConfigFile(
                parameters,
                approot + @"\php\php.ini",
                approot + @"\php\phpNew.ini");
        }

        public override void OnStop()
        {
            diagnosticMonitor.Shutdown();
            hwcServer.Stop();
            base.OnStop();
        }

        private void RoleEnvironmentChanging(object sender, RoleEnvironmentChangingEventArgs e)
        {
            if (e.Changes.Any(change => change is RoleEnvironmentConfigurationSettingChange))
            {
                e.Cancel = true;
            }
        }

        void RoleEnvironment_Stopping(object sender, RoleEnvironmentStoppingEventArgs e)
        {
            Trace.TraceWarning("workerrole shutdown happened");
        }

 

 

        #region
        private void Execute(object obj)
        {
            string result = ExecutePHP();

            //Write the result
            Trace.WriteLine(result);
        }

        /// <summary>
        /// Execute the PHP Startup Script
        /// </summary>
        /// <returns>Result</returns>  
        private string ExecutePHP()
        {
            try
            {
                Process p = new Process();

                // Get RoleRoot from Environment
                string roleRoot = Environment.GetEnvironmentVariable("RoleRoot");

                //ProcessStartInfo
              //ProcessStartInfo info = new ProcessStartInfo(@"C:\Program Files (x86)\PHP\php.exe",
                //                      RoleEnvironment.GetConfigurationSettingValue("StartupScript"));


                //ProcessStartInfo
               ProcessStartInfo info = new ProcessStartInfo(roleRoot + @"\approot\php\php.exe",
                                 roleRoot + @"\approot\" + RoleEnvironment.GetConfigurationSettingValue("StartupScript"));

                info.RedirectStandardOutput = true;
                info.RedirectStandardError = true;
                info.UseShellExecute = false;
                info.CreateNoWindow = true;

                p.StartInfo = info;

                //Events for output and error
                p.OutputDataReceived += new DataReceivedEventHandler(p_OutputDataReceived);
                p.ErrorDataReceived += new DataReceivedEventHandler(p_ErrorDataReceived);

                //Start the process
                bool isStarted = p.Start();

                p.BeginOutputReadLine();
                p.BeginErrorReadLine();

                p.WaitForExit();
                if (p.ExitCode != 0)
                {
                    throw new Exception("PHP has failed");
                }

                //Get output in String Builder
                StringBuilder sb = output.GetStringBuilder();
                string res = sb.ToString();

                //Clear the output stream
                sb.Remove(0, sb.Length);

                //Return the result
                return res;
            }
            catch (Exception ex)
            {
                Trace.WriteLine(ex.Message, "Error");
                return null;
            }
        }

        /// <summary>
        /// Error Event
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void p_ErrorDataReceived(object sender, DataReceivedEventArgs e)
        {
            //Write Error
            if (e.Data != null)
                error.WriteLine("Error: " + e.Data);
        }

        /// <summary>
        /// Output Event
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void p_OutputDataReceived(object sender, DataReceivedEventArgs e)
        {
            //Write Output
            if (e.Data != null)
                output.WriteLine(e.Data);
        }

        private void CSWRoleEnvironmentChanging(object sender, RoleEnvironmentChangingEventArgs e)
        {
            // If a configuration setting is changing
            if (e.Changes.Any(change => change is RoleEnvironmentConfigurationSettingChange))
            {
                // Set e.Cancel to true to restart this role instance
                e.Cancel = true;
            }
        }
        #endregion
    }
}